package cn.liufuwen.websocket.netty;

import com.alibaba.fastjson.JSON;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.net.URLDecoder;
import java.util.concurrent.ConcurrentHashMap;

/*
WebSocket处理程序
TextWebSocketFrame:在netty中,适用于为websocket专门处理文本的对象,frame是消息的载体
 */
@Slf4j
@Component
public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    //用于记录和管理所有客户端
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    // concurrent包的线程安全Map，用来存放每个客户端对应的MyWebSocket对象。(以后用数据库代替)
    private static ConcurrentHashMap<String, Channel> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 当从WebSocket通道读取到数据时被调用。
     * 此方法专门处理TextWebSocketFrame类型的帧，这是WebSocket协议中的一种帧类型，用于传输文本数据。
     *
     * @param ctx 通道上下文，用于通道的管理和操作，如写入数据、关闭通道等。
     * @param msg 接收到的TextWebSocketFrame对象，包含从客户端接收到的文本数据。
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        // 日志记录接收到的数据。
        log.info("接收到的数据: " + msg.text());

        // 从接收到的WebSocket帧中提取文本内容。
        //获取客户端消息
        String content = msg.text();
        // 将文本内容解析为Msgs对象，以便访问其中的属性。
        Msgs msgs = JSON.parseObject(content, Msgs.class);
        // 从Msgs对象中提取token。
        String token = msgs.getToken();
        // 根据token从webSocketMap中查找对应的Channel。
        Channel channel = webSocketMap.get(token);

        // 检查通道是否为空或不活跃（已关闭），如果是，则向调用方发送提示信息。
        if (channel == null || !channel.isActive()) {
            ctx.channel().writeAndFlush(new TextWebSocketFrame(token + " 不在线"));
        } else {
            // 如果通道存在且活跃，将消息内容写入并刷新到该通道，即发送消息给客户端。
            channel.writeAndFlush(new TextWebSocketFrame(msgs.getMsg()));
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (null != msg && msg instanceof FullHttpRequest) {
            //转化为http请求
            FullHttpRequest request = (FullHttpRequest) msg;
            //拿到请求地址
            String uri = request.uri();
            log.info("3.拿到请求地址" + uri);
            //判断是不是websocket请求，如果是拿出我们传递的参数（我的是token）
            String origin = request.headers().get("Origin");
            if (null == origin) {
                ctx.close();
            } else {
                String str = StringUtils.substringAfter(uri, "/tunnel/");
                str = URLDecoder.decode(str, "UTF-8");
                String[] strs = str.split("/");
                webSocketMap.put(strs[0], ctx.channel());
                //重新设置请求地址
                request.setUri("/tunnel");
                clients.writeAndFlush(new TextWebSocketFrame(strs[0] + "上线了"));
                clients.add(ctx.channel());

            }
        }
        //接着建立请求
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("2.channel...活跃");
        super.channelActive(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.info("channel...移除");
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channel...不活跃");
        super.channelInactive(ctx);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("4.channel...读取完毕");
        super.channelReadComplete(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        log.info("channel...用户事件触发");
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        log.info("channel...可更改");
        super.channelWritabilityChanged(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("1.助手类添加");
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("助手类移除");
        clients.remove(ctx.channel());
        autoFlush();
        super.handlerRemoved(ctx);

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("捕获到异常");
        super.exceptionCaught(ctx, cause);
    }

    public static void autoFlush() {
        log.info("移除缓存里状态无效的channel");
        webSocketMap.entrySet().removeIf(entry -> !entry.getValue().isOpen());
    }
}

